#define _GNU_SOURCE

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <execinfo.h>
#include <ucontext.h>

/*
 * backtrace和bacetrace_symbols打印出回溯信息
 */
 
#if 0
#include <execinfo.h>

int backtrace(void **buffer, int size);
char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);

#endif

#if 0
static void arm_sigsegv_handler(int sig, siginfo_t *info, void *ctx)
{
    ucontext_t *context = (ucontext_t *)ctx;
    int i = 0;
    ofstream ofs;
	ofs.open("/mnt/mtd/SIGSEGV.txt", ios::out);

    /* dump registers, ARM CPU specific */
    printf( "Signal = %d  Memory location = %p\n"
            "R0 = %016X  R1 = %016X  R2 = %016X\n"
            "R3 = %016X  R4 = %016X  R5 = %016X\n"
            "R6 = %016X  R7 = %016X\n"
            "R8 = %016X  R9 = %016X  R10 = %016X\n"
            "FP = %016X  IP = %016X  SP = %016X\n"
            "LR = %016X  PC = %016X \n\n",
            sig, info->si_addr,
            context->uc_mcontext.arm_r0,
            context->uc_mcontext.arm_r1,
            context->uc_mcontext.arm_r2,
            context->uc_mcontext.arm_r3,
            context->uc_mcontext.arm_r4,
            context->uc_mcontext.arm_r5,
            context->uc_mcontext.arm_r6,
            context->uc_mcontext.arm_r7,
            context->uc_mcontext.arm_r8,
            context->uc_mcontext.arm_r9,
            context->uc_mcontext.arm_r10,
            context->uc_mcontext.arm_fp,
            context->uc_mcontext.arm_ip,
            context->uc_mcontext.arm_sp,
            context->uc_mcontext.arm_lr,
            context->uc_mcontext.arm_pc);

    /* get call stack and write to stdout */
    void *buf[256] = {0};
    int n = backtrace(buf, 256);
    printf("backtrace() returned %d addresses", n);

    ofs << "Memory location:lr:pc" << info->si_addr << context->uc_mcontext.arm_lr << context->uc_mcontext.arm_pc << endl;	
 
    char **symbols = backtrace_symbols(buf, n);
	if(NULL == symbols){
		printf("backtrace symbols");
		exit(EXIT_FAILURE);
	}

    for (i = 0; i < n; i++) {
		printf("%d: %s\n", i, symbols[i]);
        ofs << symbols[i] << endl;
	}
 
    ofs.close();
	free(symbols);
}
#endif

static void signal_handler(int sig, siginfo_t *info, void *ctx)
{
    ucontext_t *context = (ucontext_t *)ctx;
    int i = 0;

    /* dump registers, x64 CPU specific */
    printf( "Signal = %d  Memory location = %p\n"
            "RIP = %016X  RSP = %016X  RBP = %016X\n"
            "RAX = %016X  RBX = %016X  RCX = %016X\n"
            "RDX = %016X  RSI = %016X  RDI = %016X\n"
            "R8  = %016X  R9  = %016X  R10 = %016X\n"
            "R11 = %016X  R12 = %016X  R13 = %016X\n"
            "R14 = %016X  R15 = %016X  RFLAGS = %016X\n\n",
            sig, info->si_addr,
            context->uc_mcontext.gregs[REG_RIP],
            context->uc_mcontext.gregs[REG_RSP],
            context->uc_mcontext.gregs[REG_RBP],
            context->uc_mcontext.gregs[REG_RAX],
            context->uc_mcontext.gregs[REG_RBX],
            context->uc_mcontext.gregs[REG_RCX],
            context->uc_mcontext.gregs[REG_RDX],
            context->uc_mcontext.gregs[REG_RSI],
            context->uc_mcontext.gregs[REG_RDI],
            context->uc_mcontext.gregs[REG_R8],
            context->uc_mcontext.gregs[REG_R9],
            context->uc_mcontext.gregs[REG_R10],
            context->uc_mcontext.gregs[REG_R11],
            context->uc_mcontext.gregs[REG_R12],
            context->uc_mcontext.gregs[REG_R13],
            context->uc_mcontext.gregs[REG_R14],
            context->uc_mcontext.gregs[REG_R15],
            context->uc_mcontext.gregs[REG_EFL]);

    /* get call stack and write to stdout */
    void *buf[256] = {0};
    int n = backtrace(buf, 256);

    #if 0
    backtrace_symbols_fd(buf, n, fileno(stdout));
    #endif
    
    #if 1
    char **symbols = backtrace_symbols(buf, n);
	if(NULL == symbols){
		perror("backtrace symbols");
		exit(EXIT_FAILURE);
	}
	printf("[%s]:[%d]\n", __func__, __LINE__);
	for (i = 0; i < n; i++) {
		printf("%d: %s\n", i, symbols[i]);
	}
    #endif
 
	free(symbols);

    exit(-1);
}

static void gdb_signal_handler(int sig, siginfo_t *info, void *ctx)
{
    char cmd[256];

    printf("Segmentation fault happened, starting GDB ... \n\n");

    snprintf(cmd, 256, "gdb --pid=%d -ex bt -q", getpid());
    system(cmd);

    printf("Finish debugging, now quit! \n");
    exit(-1);
}

void test_3(int *p)
{
    *p = 1;
}

void test_2(int *p)
{
    test_3(p);
}

void test_1(int *p)
{
    test_2(p);
}

int main(int argc, char *argv[])
{
    int *p = 0x12345678;

    struct sigaction action;
    sigemptyset(&action.sa_mask);    
    //action.sa_sigaction = signal_handler;
    //action.sa_sigaction = arm_signal_handler;
    action.sa_sigaction = gdb_signal_handler;
    action.sa_flags = SA_SIGINFO;

    sigaction(SIGSEGV, &action, NULL);

    test_1(p);
    return 0;
}

// gcc -rdynamic backtrace2.c -o backtrace2


#if 0

000000000000143f <test_3>:
    143f:	f3 0f 1e fa          	endbr64 
    1443:	55                   	push   %rbp
    1444:	48 89 e5             	mov    %rsp,%rbp
    1447:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
    144b:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
    144f:	c7 00 01 00 00 00    	movl   $0x1,(%rax)                 // SIGSEGV
    1455:	90                   	nop
    1456:	5d                   	pop    %rbp
    1457:	c3                   	retq   

0000000000001458 <test_2>:
    1458:	f3 0f 1e fa          	endbr64 
    145c:	55                   	push   %rbp
    145d:	48 89 e5             	mov    %rsp,%rbp
    1460:	48 83 ec 08          	sub    $0x8,%rsp
    1464:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
    1468:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
    146c:	48 89 c7             	mov    %rax,%rdi
    146f:	e8 cb ff ff ff       	callq  143f <test_3>
    1474:	90                   	nop
    1475:	c9                   	leaveq 
    1476:	c3                   	retq   

0000000000001477 <test_1>:
    1477:	f3 0f 1e fa          	endbr64 
    147b:	55                   	push   %rbp
    147c:	48 89 e5             	mov    %rsp,%rbp
    147f:	48 83 ec 08          	sub    $0x8,%rsp
    1483:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
    1487:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
    148b:	48 89 c7             	mov    %rax,%rdi
    148e:	e8 c5 ff ff ff       	callq  1458 <test_2>
    1493:	90                   	nop
    1494:	c9                   	leaveq 
    1495:	c3                   	retq   

Signal = 11  Memory location = 0x12345678
RIP = 000000000C0E744F  RSP = 00000000029CAB30  RBP = 00000000029CAB30
RAX = 0000000012345678  RBX = 000000000C0E7540  RCX = 00000000317BE166
RDX = 0000000000000000  RSI = 00000000029CAA20  RDI = 0000000012345678
R8  = 0000000000000000  R9  = 0000000000000000  R10 = 0000000000000008
R11 = 0000000000000246  R12 = 000000000C0E7140  R13 = 00000000029CAD20
R14 = 0000000000000000  R15 = 0000000000000000  RFLAGS = 0000000000010202

./backtrace2(+0x1407)[0x56150c0e7407]           // backtrace
/lib/x86_64-linux-gnu/libc.so.6(+0x43090)[0x7f38317be090]
./backtrace2(test_3+0x10)[0x56150c0e744f]       // SIGSEGV
./backtrace2(test_2+0x1c)[0x56150c0e7474]       // test_2 -> test3
./backtrace2(test_1+0x1c)[0x56150c0e7493]       // test_1 -> test2
./backtrace2(main+0x86)[0x56150c0e751c]         // main -> test_1
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x7f383179f083]
./backtrace2(_start+0x2e)[0x56150c0e716e]

#endif


